--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / backend / database / __init__.py
Displaying Raw • Download
meshchatx/src/backend/database/__init__.py 6d6b40536aa49435a195fce36c4c3e4b8d96c738 (6d6b4053) Text, 41.09 KB
T8b949e# SPDX-License-Identifier: 0BSD
Tff7b72import T7ee787json
Tff7b72import T7ee787logging
Tff7b72import T7ee787os
Tff7b72import T7ee787re
Tff7b72import T7ee787shutil
Tff7b72import T7ee787tempfile
Tff7b72import T7ee787time
Tff7b72import T7ee787zipfile
Tff7b72from T7ee787contextlib Tff7b72import Te6edf3suppress
Tff7b72from T7ee787datetime Tff7b72import Te6edf3UTCTb4b4b4, Te6edf3datetime
Tff7b72from T7ee787.T7ee787access_attempts Tff7b72import Te6edf3AccessAttemptsDAO
Tff7b72from T7ee787.T7ee787announces Tff7b72import Te6edf3AnnounceDAO
Tff7b72from T7ee787.T7ee787config Tff7b72import Te6edf3ConfigDAO
Tff7b72from T7ee787.T7ee787contacts Tff7b72import Te6edf3ContactsDAO
Tff7b72from T7ee787.T7ee787crash_history Tff7b72import Te6edf3CrashHistoryDAO
Tff7b72from T7ee787.T7ee787debug_logs Tff7b72import Te6edf3DebugLogsDAO
Tff7b72from T7ee787.T7ee787gifs Tff7b72import Te6edf3UserGifsDAO
Tff7b72from T7ee787.T7ee787map_drawings Tff7b72import Te6edf3MapDrawingsDAO
Tff7b72from T7ee787.T7ee787map_overlays Tff7b72import Te6edf3MapOverlaysDAO
Tff7b72from T7ee787.T7ee787messages Tff7b72import Te6edf3MessageDAO
Tff7b72from T7ee787.T7ee787misc Tff7b72import Te6edf3MiscDAO
Tff7b72from T7ee787.T7ee787notification_sounds Tff7b72import Te6edf3NotificationSoundDAO
Tff7b72from T7ee787.T7ee787provider Tff7b72import Te6edf3DatabaseProvider
Tff7b72from T7ee787.T7ee787ringtones Tff7b72import Te6edf3RingtoneDAO
Tff7b72from T7ee787.T7ee787rrc_room_keys Tff7b72import Te6edf3RrcRoomKeysDAO
Tff7b72from T7ee787.T7ee787schema Tff7b72import Tb4b4b4(
Te6edf3DatabaseSchemaTb4b4b4,
Te6edf3DatabaseTooNewErrorTb4b4b4,
Te6edf3PostMigrationVerificationErrorTb4b4b4,
Te6edf3PreMigrationBackupErrorTb4b4b4,
Tb4b4b4)
Tff7b72from T7ee787.T7ee787sticker_packs Tff7b72import Te6edf3UserStickerPacksDAO
Tff7b72from T7ee787.T7ee787stickers Tff7b72import Te6edf3UserStickersDAO
Tff7b72from T7ee787.T7ee787telemetry Tff7b72import Te6edf3TelemetryDAO
Tff7b72from T7ee787.T7ee787telephone Tff7b72import Te6edf3TelephoneDAO
Tff7b72from T7ee787.T7ee787voicemails Tff7b72import Te6edf3VoicemailDAO
Te6edf3BACKUP_BASELINE_FILENAME Tff7b72= Ta5d6ff"Ta5d6ffbackup-baseline.jsonTa5d6ff"
Te6edf3BACKUP_MANIFEST_NAME Tff7b72= Ta5d6ff"Ta5d6ffbackup-manifest.jsonTa5d6ff"
Te6edf3BACKUP_SKIP_DIR_NAMES Tff7b72= Tffa657frozensetTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffsnapshotsTa5d6ff"Tb4b4b4}Tb4b4b4)
Te6edf3PRE_MIGRATE_BACKUP_PREFIX Tff7b72= Ta5d6ff"Ta5d6ffbackup-pre-migrate-Ta5d6ff"
Te6edf3MIN_SIZE_RATIO Tff7b72= T79c0ff0.2
Te6edf3_log Tff7b72= Te6edf3loggingTff7b72.Td2a8ffgetLoggerTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.databaseTa5d6ff"Tb4b4b4)
Tff7b72class T56d364DatabaseRestoreErrorTb4b4b4(Tf85149RuntimeErrorTb4b4b4)Tb4b4b4:
Tff7b72pass
Te6edf3_PRAGMA_READ_NAME_RE Tff7b72= Te6edf3reTff7b72.Td2a8ffcompileTb4b4b4(Ta5d6ffrTa5d6ff"Ta5d6ff^[A-Za-z_][A-Za-z0-9_]*Ta5d6ff\Ta5d6ffZTa5d6ff"Tb4b4b4)
Te6edf3_ALLOWED_WAL_CHECKPOINT_MODES Tff7b72= Tffa657frozensetTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffPASSIVETa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffFULLTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRESTARTTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTRUNCATETa5d6ff"Tb4b4b4}Tb4b4b4)
Tff7b72def Td2a8ff_sanitize_pragma_read_nameTb4b4b4(Te6edf3nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Allow only simple SQLite pragma tokens for dynamic PRAGMA name reads."""
Tff7b72if Tff7b72not Te6edf3name Tff7b72or Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3nameTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3token Tff7b72= Te6edf3nameTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3token Tff7b72or Tff7b72not Te6edf3_PRAGMA_READ_NAME_RETff7b72.Td2a8ffmatchTb4b4b4(Te6edf3tokenTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3token
Tff7b72def Td2a8ff_sanitize_wal_checkpoint_modeTb4b4b4(Te6edf3modeTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3modeTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffWAL checkpoint mode must be a stringTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3m Tff7b72= Te6edf3modeTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffupperTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3m Tff7b72not Tff7b72in Te6edf3_ALLOWED_WAL_CHECKPOINT_MODESTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffInvalid WAL checkpoint mode: Tffd700{Te6edf3modeTffd700!r}Ta5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72return Te6edf3m
Tff7b72def Td2a8ff_pre_migrate_backup_keepTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657intTb4b4b4:
Te6edf3raw Tff7b72= Te6edf3osTff7b72.Td2a8ffenvironTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_PRE_MIGRATE_BACKUP_KEEPTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff5Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72return Tffa657maxTb4b4b4(T79c0ff0Tb4b4b4, Tffa657intTb4b4b4(Te6edf3rawTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Tff7b72return T79c0ff5
Tff7b72class T56d364DatabaseTb4b4b4:
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3db_pathTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffprovider Tff7b72= Te6edf3DatabaseProviderTff7b72.Td2a8ffget_instanceTb4b4b4(Te6edf3db_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffschema Tff7b72= Te6edf3DatabaseSchemaTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffconfig Tff7b72= Te6edf3ConfigDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmessages Tff7b72= Te6edf3MessageDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffannounces Tff7b72= Te6edf3AnnounceDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmisc Tff7b72= Te6edf3MiscDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8fftelephone Tff7b72= Te6edf3TelephoneDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8fftelemetry Tff7b72= Te6edf3TelemetryDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffvoicemails Tff7b72= Te6edf3VoicemailDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffringtones Tff7b72= Te6edf3RingtoneDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffnotification_sounds Tff7b72= Te6edf3NotificationSoundDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffcontacts Tff7b72= Te6edf3ContactsDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmap_drawings Tff7b72= Te6edf3MapDrawingsDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmap_overlays Tff7b72= Te6edf3MapOverlaysDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffstickers Tff7b72= Te6edf3UserStickersDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffsticker_packs Tff7b72= Te6edf3UserStickerPacksDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffgifs Tff7b72= Te6edf3UserGifsDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffdebug_logs Tff7b72= Te6edf3DebugLogsDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffaccess_attempts Tff7b72= Te6edf3AccessAttemptsDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffcrash_history Tff7b72= Te6edf3CrashHistoryDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffrrc_room_keys Tff7b72= Te6edf3RrcRoomKeysDAOTb4b4b4(Tff7b72selfTff7b72.Td2a8ffproviderTb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_sqlite_memory_relaxed Tff7b72= Tff7b72False
Tff7b72selfTff7b72.Td2a8ff_last_pre_migrate_backup_pathTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72None
Tff7b72def Td2a8ffinitializeTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_tune_sqlite_pragmasTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffschemaTff7b72.Td2a8ff_create_initial_tablesTb4b4b4(Tb4b4b4)
Te6edf3current_version Tff7b72= Tff7b72selfTff7b72.Td2a8ffschemaTff7b72.Td2a8ffget_current_versionTb4b4b4(Tb4b4b4)
Te6edf3target_version Tff7b72= Te6edf3DatabaseSchemaTff7b72.Td2a8ffLATEST_VERSION
Te6edf3backup_pathTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72None
Te6edf3started Tff7b72= Te6edf3timeTff7b72.Td2a8ffmonotonicTb4b4b4(Tb4b4b4)
Te6edf3from_version Tff7b72= Te6edf3current_version
Tff7b72if Te6edf3current_version Tff7b72> Te6edf3target_versionTb4b4b4:
Te6edf3msg Tff7b72= Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffDatabase version Tffd700{Te6edf3current_versionTffd700}Ta5d6ff is newer than this MeshChatX build Ta5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ffsupports (Tffd700{Te6edf3target_versionTffd700}Ta5d6ff). Restore a backup or upgrade the application.Ta5d6ff"
Tb4b4b4)
Tff7b72raise Te6edf3DatabaseTooNewErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if T79c0ff0 Tff7b72< Te6edf3current_version Tff7b72< Te6edf3target_versionTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787env_utils Tff7b72import Te6edf3env_bool
Tff7b72if Tff7b72not Te6edf3env_boolTb4b4b4(Ta5d6ff"Ta5d6ffMESHCHAT_SKIP_PRE_MIGRATE_BACKUPTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3result Tff7b72= Tff7b72selfTff7b72.Td2a8ff_backup_pre_migrationTb4b4b4(
Te6edf3current_versionTb4b4b4,
Te6edf3target_versionTb4b4b4,
Tb4b4b4)
Te6edf3backup_path Tff7b72= Te6edf3resultTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_last_pre_migrate_backup_path Tff7b72= Te6edf3backup_path
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3msg Tff7b72= Tb4b4b4(
Ta5d6ff"Ta5d6ffPre-migration backup failed; aborting schema upgrade. Ta5d6ff"
Ta5d6ff"Ta5d6ffFix disk space or permissions, or set Ta5d6ff"
Ta5d6ff"Ta5d6ffMESHCHAT_SKIP_PRE_MIGRATE_BACKUP=1: Ta5d6ff"
Ta5d6fffTa5d6ff"Tffd700{Te6edf3excTffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72raise Te6edf3PreMigrationBackupErrorTb4b4b4(Te6edf3msgTb4b4b4) Tff7b72from T7ee787exc
Tff7b72selfTff7b72.Td2a8ffschemaTff7b72.Td2a8ffmigrateTb4b4b4(
Te6edf3current_versionTb4b4b4,
Te6edf3target_versionTb4b4b4,
Te6edf3update_versionTff7b72=Tff7b72FalseTb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_verify_after_migrationTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffschemaTff7b72.Td2a8ff_update_database_version_toTb4b4b4(Te6edf3target_versionTb4b4b4)
Te6edf3keep Tff7b72= Te6edf3_pre_migrate_backup_keepTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3keep Tff7b72> T79c0ff0Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_prune_pre_migrate_backupsTb4b4b4(
Tff7b72selfTff7b72.Td2a8ff_identity_storage_dirTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3keepTff7b72=Te6edf3keepTb4b4b4,
Te6edf3preserve_pathTff7b72=Te6edf3backup_pathTb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Te6edf3duration_ms Tff7b72= Tffa657intTb4b4b4(Tb4b4b4(Te6edf3timeTff7b72.Td2a8ffmonotonicTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3startedTb4b4b4) Tff7b72* T79c0ff1000Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffinfoTb4b4b4(
Ta5d6ff"Ta5d6ffschema_migration from=Tffd700%sTa5d6ff to=Tffd700%sTa5d6ff backup=Tffd700%sTa5d6ff duration_ms=Tffd700%sTa5d6ff status=failed error=Tffd700%sTa5d6ff"Tb4b4b4,
Te6edf3from_versionTb4b4b4,
Te6edf3target_versionTb4b4b4,
Te6edf3backup_path Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4,
Te6edf3duration_msTb4b4b4,
Tffa657typeTb4b4b4(Te6edf3excTb4b4b4)Tff7b72.Td2a8ff__name__Tb4b4b4,
Tb4b4b4)
Tff7b72raise
Tff7b72elseTb4b4b4:
Te6edf3duration_ms Tff7b72= Tffa657intTb4b4b4(Tb4b4b4(Te6edf3timeTff7b72.Td2a8ffmonotonicTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3startedTb4b4b4) Tff7b72* T79c0ff1000Tb4b4b4)
Tff7b72if Te6edf3from_version Tff7b72< Te6edf3target_versionTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffinfoTb4b4b4(
Ta5d6ff"Ta5d6ffschema_migration from=Tffd700%sTa5d6ff to=Tffd700%sTa5d6ff backup=Tffd700%sTa5d6ff duration_ms=Tffd700%sTa5d6ff status=okTa5d6ff"Tb4b4b4,
Te6edf3from_versionTb4b4b4,
Te6edf3target_versionTb4b4b4,
Te6edf3backup_path Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4,
Te6edf3duration_msTb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8ff_verify_after_migrationTb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Te6edf3rows Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffquick_checkTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3rowsTb4b4b4:
Te6edf3first Tff7b72= Te6edf3rowsTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3val Tff7b72= Tffa657nextTb4b4b4(Tffa657iterTb4b4b4(Te6edf3firstTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4) Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3firstTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Te6edf3firstTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72if Te6edf3val Tff7b72!= Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffPost-migration quick_check failed: Tffd700{Te6edf3valTffd700!s}Ta5d6ff"
Tff7b72raise Te6edf3PostMigrationVerificationErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(Ta5d6ff"Ta5d6ffSELECT 1Ta5d6ff"Tb4b4b4)
Tf0883e@staticmethod
Tff7b72def Td2a8ff_prune_pre_migrate_backupsTb4b4b4(
Te6edf3storage_pathTb4b4b4: Tffa657strTb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3keepTb4b4b4: Tffa657intTb4b4b4,
Te6edf3preserve_pathTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3keep Tff7b72<Tff7b72= T79c0ff0Tb4b4b4:
Tff7b72return
Te6edf3backup_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3backup_dirTb4b4b4)Tb4b4b4:
Tff7b72return
Te6edf3preserve_abs Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3preserve_pathTb4b4b4) Tff7b72if Te6edf3preserve_path Tff7b72else Tff7b72None
Te6edf3entriesTb4b4b4: Tffa657listTb4b4b4[Tffa657tupleTb4b4b4[Tffa657strTb4b4b4, Tffa657floatTb4b4b4]Tb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3name Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3backup_dirTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3nameTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3PRE_MIGRATE_BACKUP_PREFIXTb4b4b4) Tff7b72or Tff7b72not Te6edf3nameTff7b72.Td2a8ffendswithTb4b4b4(
Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3full Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3backup_dirTb4b4b4, Te6edf3nameTb4b4b4)
Te6edf3entriesTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4(Te6edf3fullTb4b4b4, Te6edf3osTff7b72.Td2a8ffstatTb4b4b4(Te6edf3fullTb4b4b4)Tff7b72.Td2a8ffst_mtimeTb4b4b4)Tb4b4b4)
Te6edf3entriesTff7b72.Td2a8ffsortTb4b4b4(Te6edf3keyTff7b72=Tff7b72lambda Te6edf3itemTb4b4b4: Te6edf3itemTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)
Te6edf3idx Tff7b72= T79c0ff0
Tff7b72while Tffa657lenTb4b4b4(Te6edf3entriesTb4b4b4) Tff7b72> Te6edf3keep Tff7b72and Te6edf3idx Tff7b72< Tffa657lenTb4b4b4(Te6edf3entriesTb4b4b4)Tb4b4b4:
Te6edf3pathTb4b4b4, Te6edf3_mtime Tff7b72= Te6edf3entriesTb4b4b4[Te6edf3idxTb4b4b4]
Tff7b72if Te6edf3preserve_abs Tff7b72and Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3pathTb4b4b4) Tff7b72== Te6edf3preserve_absTb4b4b4:
Te6edf3idx Tff7b72+Tff7b72= T79c0ff1
Tff7b72continue
Tff7b72with Te6edf3suppressTb4b4b4(Tf85149OSErrorTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3pathTb4b4b4)
Te6edf3entriesTff7b72.Td2a8ffpopTb4b4b4(Te6edf3idxTb4b4b4)
Tff7b72def Td2a8ffexecute_sqlTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3queryTb4b4b4, Te6edf3paramsTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(Te6edf3queryTb4b4b4, Te6edf3paramsTb4b4b4)
Tff7b72def Td2a8ff_ensure_sqlite_temp_dirTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Prefer a storage-local temp dir so Landlock can open spill files."""
Tff7b72tryTb4b4b4:
Te6edf3db_dir Tff7b72= Tff7b72selfTff7b72.Td2a8ff_identity_storage_dirTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Tff7b72not Te6edf3db_dirTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3temp_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3db_dirTb4b4b4, Ta5d6ff"Ta5d6ffsqlite-tmpTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3temp_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72return Tff7b72None
T8b949e# SQLite uses TMPDIR for PRAGMA temp_store=FILE spill files.
Te6edf3osTff7b72.Td2a8ffenvironTb4b4b4[Ta5d6ff"Ta5d6ffTMPDIRTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3temp_dir
Te6edf3osTff7b72.Td2a8ffenvironTb4b4b4[Ta5d6ff"Ta5d6ffTMPTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3temp_dir
Te6edf3osTff7b72.Td2a8ffenvironTb4b4b4[Ta5d6ff"Ta5d6ffTEMPTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3temp_dir
Tff7b72return Te6edf3temp_dir
Tff7b72def Td2a8ff_tune_sqlite_pragmasTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffprefer_temp_store_file Tff7b72= Tff7b72False
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA journal_mode=WALTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA synchronous=NORMALTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA wal_autocheckpoint=1000Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA temp_store=MEMORYTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA cache_size=-8000Ta5d6ff"Tb4b4b4) T8b949e# 8 MB
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA mmap_size=67108864Ta5d6ff"Tb4b4b4) T8b949e# 64 MB
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA busy_timeout=5000Ta5d6ff"Tb4b4b4) T8b949e# 5 s wait on lock contention
Tff7b72selfTff7b72.Td2a8ff_sqlite_memory_relaxed Tff7b72= Tff7b72False
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffSQLite pragma setup failed: Tffd700{Te6edf3excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffapply_memory_pressure_pragmasTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3relaxTb4b4b4: Tffa657boolTb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3landlock_activeTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4,
Te6edf3fs_sandbox_activeTb4b4b4: Tffa657bool Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Shrink SQLite cache under low RAM.
FILE temp spills break complex conversation queries under filesystem
sandboxes (Landlock or Windows AppContainer), even when TMPDIR is
inside the allowed storage tree. Keep MEMORY temp while a FS sandbox
is active and only reduce cache/mmap. Without a sandbox, FILE temp
is still used.
"""
Te6edf3sandbox_active Tff7b72= Tb4b4b4(
Te6edf3landlock_active Tff7b72if Te6edf3fs_sandbox_active Tff7b72is Tff7b72None Tff7b72else Tffa657boolTb4b4b4(Te6edf3fs_sandbox_activeTb4b4b4)
Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3relaxTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_ensure_sqlite_temp_dirTb4b4b4(Tb4b4b4)
Te6edf3use_file_temp Tff7b72= Tff7b72not Te6edf3sandbox_active
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffprefer_temp_store_file Tff7b72= Te6edf3use_file_temp
Tff7b72if Te6edf3use_file_tempTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA temp_store=FILETa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA temp_store=MEMORYTa5d6ff"Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffinfoTb4b4b4(
Ta5d6ff"Ta5d6ffMemory pressure under FS sandbox: keeping temp_store=MEMORYTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA cache_size=-2000Ta5d6ff"Tb4b4b4) T8b949e# 2 MB
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA mmap_size=0Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_sqlite_memory_relaxed Tff7b72= Tff7b72True
Tff7b72elseTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffprefer_temp_store_file Tff7b72= Tff7b72False
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA temp_store=MEMORYTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA cache_size=-8000Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffPRAGMA mmap_size=67108864Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_sqlite_memory_relaxed Tff7b72= Tff7b72False
Tff7b72return Tff7b72True
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffSQLite memory-pressure pragma update failed: Tffd700{Te6edf3excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_get_pragma_valueTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3pragmaTb4b4b4: Tffa657strTb4b4b4, Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Te6edf3safe Tff7b72= Te6edf3_sanitize_pragma_read_nameTb4b4b4(Te6edf3pragmaTb4b4b4)
Tff7b72if Te6edf3safe Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3default
Tff7b72tryTb4b4b4:
Te6edf3cursor Tff7b72= Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffPRAGMA Tffd700{Te6edf3safeTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3row Tff7b72= Te6edf3cursorTff7b72.Td2a8fffetchoneTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3row Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3default
Tff7b72return Te6edf3rowTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3default
Tff7b72def Td2a8ff_get_database_file_statsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72def Td2a8ffsize_forTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72return T79c0ff0
Te6edf3db_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffdb_path
Te6edf3wal_path Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3db_pathTffd700}Ta5d6ff-walTa5d6ff"
Te6edf3shm_path Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3db_pathTffd700}Ta5d6ff-shmTa5d6ff"
Te6edf3main_bytes Tff7b72= Te6edf3size_forTb4b4b4(Te6edf3db_pathTb4b4b4)
Te6edf3wal_bytes Tff7b72= Te6edf3size_forTb4b4b4(Te6edf3wal_pathTb4b4b4)
Te6edf3shm_bytes Tff7b72= Te6edf3size_forTb4b4b4(Te6edf3shm_pathTb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffmain_bytesTa5d6ff"Tb4b4b4: Te6edf3main_bytesTb4b4b4,
Ta5d6ff"Ta5d6ffwal_bytesTa5d6ff"Tb4b4b4: Te6edf3wal_bytesTb4b4b4,
Ta5d6ff"Ta5d6ffshm_bytesTa5d6ff"Tb4b4b4: Te6edf3shm_bytesTb4b4b4,
Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4: Te6edf3main_bytes Tff7b72+ Te6edf3wal_bytes Tff7b72+ Te6edf3shm_bytesTb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ff_get_backup_baseline_pathTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4)Tb4b4b4:
Te6edf3default_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3default_dirTb4b4b4, Te6edf3BACKUP_BASELINE_FILENAMETb4b4b4)
Tff7b72def Td2a8ff_read_backup_baselineTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_backup_baseline_pathTb4b4b4(Te6edf3storage_pathTb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Tff7b72return Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149OSErrorTb4b4b4, Te6edf3jsonTff7b72.Td2a8ffJSONDecodeErrorTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72def Td2a8ff_write_backup_baselineTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4, Te6edf3message_countTb4b4b4, Te6edf3total_bytesTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_backup_baseline_pathTb4b4b4(Te6edf3storage_pathTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3data Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4: Te6edf3message_countTb4b4b4,
Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4: Te6edf3total_bytesTb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72with Tffa657openTb4b4b4(Te6edf3pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3dataTb4b4b4, Te6edf3fTb4b4b4, Te6edf3indentTff7b72=T79c0ff2Tb4b4b4)
Tff7b72except Tf85149OSError Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to write backup baseline: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_get_current_db_content_statsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3stats Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_database_file_statsTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3message_count Tff7b72= Tff7b72selfTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffcount_lxmf_messagesTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3message_count Tff7b72= Tff7b72-T79c0ff1
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4: Te6edf3message_countTb4b4b4,
Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4: Te6edf3statsTb4b4b4[Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ff_is_backup_suspiciousTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3current_statsTb4b4b4, Te6edf3baselineTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3baselineTb4b4b4:
Tff7b72return Tff7b72False
Te6edf3prev_count Tff7b72= Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3prev_bytes Tff7b72= Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3curr_count Tff7b72= Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3curr_bytes Tff7b72= Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Tff7b72if Te6edf3prev_count Tff7b72> T79c0ff0 Tff7b72and Te6edf3curr_count Tff7b72== T79c0ff0Tb4b4b4:
Tff7b72return Tff7b72True
Tff7b72if Te6edf3prev_bytes Tff7b72> T79c0ff100_000 Tff7b72and Te6edf3curr_bytes Tff7b72< Te6edf3prev_bytes Tff7b72* Te6edf3MIN_SIZE_RATIOTb4b4b4:
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffcheck_db_health_at_openTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4, Tff7b72*Tb4b4b4, Te6edf3quickTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4)Tb4b4b4:
T8b949e"""Run integrity and baseline checks after opening the database.
Returns human-readable issue strings. Empty if healthy.
When quick is True, use PRAGMA quick_check instead of full integrity_check.
"""
Te6edf3issues Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72tryTb4b4b4:
Te6edf3integrity_rows Tff7b72= Tb4b4b4(
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffquick_checkTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3quick
Tff7b72else Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffintegrity_checkTb4b4b4(Tb4b4b4)
Tb4b4b4)
Te6edf3check_label Tff7b72= Ta5d6ff"Ta5d6ffquick checkTa5d6ff" Tff7b72if Te6edf3quick Tff7b72else Ta5d6ff"Ta5d6ffintegrity checkTa5d6ff"
Tff7b72if Tff7b72not Te6edf3integrity_rowsTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDatabase Tffd700{Te6edf3check_labelTffd700}Ta5d6ff failed: no resultTa5d6ff"Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB open health check: no resultTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3first Tff7b72= Te6edf3integrity_rowsTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3val Tff7b72= Tb4b4b4(
Tffa657nextTb4b4b4(Tffa657iterTb4b4b4(Te6edf3firstTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4) Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3firstTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Te6edf3firstTb4b4b4[T79c0ff0Tb4b4b4]
Tb4b4b4)
Tff7b72if Te6edf3val Tff7b72!= Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDatabase Tffd700{Te6edf3check_labelTffd700}Ta5d6ff failed: Tffd700{Te6edf3valTffd700!s}Ta5d6ff"Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB open health check: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3valTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffDatabase integrity check error: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB open health check: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3msgTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3current Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_current_db_content_statsTb4b4b4(Tb4b4b4)
Te6edf3baseline Tff7b72= Tff7b72selfTff7b72.Td2a8ff_read_backup_baselineTb4b4b4(Te6edf3storage_pathTb4b4b4)
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_is_backup_suspiciousTb4b4b4(Te6edf3currentTb4b4b4, Te6edf3baselineTb4b4b4)Tb4b4b4:
Te6edf3prev_c Tff7b72= Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3prev_b Tff7b72= Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3curr_c Tff7b72= Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3curr_b Tff7b72= Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3msg Tff7b72= Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffDatabase content anomaly: was Tffd700{Te6edf3prev_cTffd700}Ta5d6ff messages / Tffd700{Te6edf3prev_bTffd700}Ta5d6ff bytes, Ta5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ffnow Tffd700{Te6edf3curr_cTffd700}Ta5d6ff / Tffd700{Te6edf3curr_bTffd700}Ta5d6ff. Restore from backup if needed.Ta5d6ff"
Tb4b4b4)
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB open health check: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3msgTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffinfoTb4b4b4(
Ta5d6ff"Ta5d6ffDB open health check ok: messages=Tffd700%sTa5d6ff total_bytes=Tffd700%sTa5d6ff"Tb4b4b4,
Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB open health check (baseline): Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Tff7b72return Te6edf3issues
Tff7b72def Td2a8ffcheck_db_health_at_closeTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4)Tb4b4b4:
T8b949e"""Run health checks before closing the database (for logging only).
Returns issue strings. Empty if healthy.
"""
Te6edf3issues Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72tryTb4b4b4:
Te6edf3integrity_rows Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffintegrity_checkTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3integrity_rowsTb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6ff"Ta5d6ffDatabase integrity check failed: no resultTa5d6ff"Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB close health check: no resultTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3first Tff7b72= Te6edf3integrity_rowsTb4b4b4[T79c0ff0Tb4b4b4]
Te6edf3val Tff7b72= Tb4b4b4(
Tffa657nextTb4b4b4(Tffa657iterTb4b4b4(Te6edf3firstTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4) Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3firstTb4b4b4, Tffa657dictTb4b4b4) Tff7b72else Te6edf3firstTb4b4b4[T79c0ff0Tb4b4b4]
Tb4b4b4)
Tff7b72if Te6edf3val Tff7b72!= Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDatabase integrity check failed: Tffd700{Te6edf3valTffd700!s}Ta5d6ff"Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB close health check: integrity failedTa5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB close health check: Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3current Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_current_db_content_statsTb4b4b4(Tb4b4b4)
Te6edf3baseline Tff7b72= Tff7b72selfTff7b72.Td2a8ff_read_backup_baselineTb4b4b4(Te6edf3storage_pathTb4b4b4)
Tff7b72if Tff7b72selfTff7b72.Td2a8ff_is_backup_suspiciousTb4b4b4(Te6edf3currentTb4b4b4, Te6edf3baselineTb4b4b4)Tb4b4b4:
Te6edf3issuesTff7b72.Td2a8ffappendTb4b4b4(
Ta5d6ff"Ta5d6ffDatabase content anomaly detected at close. Consider restoring from backup.Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB close health check: content anomalyTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffinfoTb4b4b4(
Ta5d6ff"Ta5d6ffDB close health check ok: messages=Tffd700%sTa5d6ff total_bytes=Tffd700%sTa5d6ff"Tb4b4b4,
Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3currentTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(Ta5d6ff"Ta5d6ffDB close health check (baseline): Tffd700%sTa5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Tff7b72return Te6edf3issues
Tff7b72def Td2a8ff_database_pathsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3db_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffdb_path
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4: Te6edf3db_pathTb4b4b4,
Ta5d6ff"Ta5d6ffwalTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Tffd700{Te6edf3db_pathTffd700}Ta5d6ff-walTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffshmTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Tffd700{Te6edf3db_pathTffd700}Ta5d6ff-shmTa5d6ff"Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ffget_database_health_snapshotTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3page_size Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffpage_sizeTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4) Tff7b72or T79c0ff0
Te6edf3page_count Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffpage_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4) Tff7b72or T79c0ff0
Te6edf3freelist_pages Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6fffreelist_countTa5d6ff"Tb4b4b4, T79c0ff0Tb4b4b4) Tff7b72or T79c0ff0
Te6edf3freelist_bytes Tff7b72= Tb4b4b4(
Te6edf3page_size Tff7b72* Te6edf3freelist_pages Tff7b72if Te6edf3page_size Tff7b72> T79c0ff0 Tff7b72and Te6edf3freelist_pages Tff7b72> T79c0ff0 Tff7b72else T79c0ff0
Tb4b4b4)
Tff7b72if Te6edf3freelist_bytes Tff7b72> T79c0ff0Tb4b4b4:
Te6edf3free_bytes Tff7b72= Te6edf3freelist_bytes
Tff7b72elseTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3db_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffdb_path
Tff7b72if Tff7b72not Te6edf3db_pathTb4b4b4:
Te6edf3free_bytes Tff7b72= T79c0ff0
Tff7b72elseTb4b4b4:
Te6edf3db_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3db_pathTb4b4b4)Tb4b4b4)
Te6edf3free_bytes Tff7b72= Te6edf3shutilTff7b72.Td2a8ffdisk_usageTb4b4b4(Te6edf3db_dirTb4b4b4)Tff7b72.Td2a8fffree Tff7b72if Te6edf3db_dir Tff7b72else T79c0ff0
Tff7b72except Tf85149OSErrorTb4b4b4:
Te6edf3free_bytes Tff7b72= T79c0ff0
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffquick_checkTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffquick_checkTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffunknownTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffjournal_modeTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffjournal_modeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffunknownTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffsynchronousTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffsynchronousTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffwal_autocheckpointTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffwal_autocheckpointTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffauto_vacuumTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffauto_vacuumTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpage_sizeTa5d6ff"Tb4b4b4: Te6edf3page_sizeTb4b4b4,
Ta5d6ff"Ta5d6ffpage_countTa5d6ff"Tb4b4b4: Te6edf3page_countTb4b4b4,
Ta5d6ff"Ta5d6fffreelist_pagesTa5d6ff"Tb4b4b4: Te6edf3freelist_pagesTb4b4b4,
Ta5d6ff"Ta5d6ffestimated_free_bytesTa5d6ff"Tb4b4b4: Te6edf3free_bytesTb4b4b4,
Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_database_file_statsTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ff_checkpoint_walTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3modeTb4b4b4: Tffa657str Tff7b72= Ta5d6ff"Ta5d6ffTRUNCATETa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3safe_mode Tff7b72= Te6edf3_sanitize_wal_checkpoint_modeTb4b4b4(Te6edf3modeTb4b4b4)
Tff7b72return Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffPRAGMA wal_checkpoint(Tffd700{Te6edf3safe_modeTffd700}Ta5d6ff)Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8fffetchallTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffrun_database_vacuumTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
T8b949e# Attempt to checkpoint WAL, ignore errors if busy
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_checkpoint_walTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffWarning: WAL checkpoint during vacuum failed (non-critical): Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffexecute_sqlTb4b4b4(Ta5d6ff"Ta5d6ffVACUUMTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_tune_sqlite_pragmasTb4b4b4(Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffhealthTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ffget_database_health_snapshotTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
T8b949e# Wrap in a cleaner error message
Tff7b72raise Tf85149ExceptionTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffDatabase vacuum failed: Tffd700{Te6edf3eTffd700!s}Ta5d6ff"Tb4b4b4) Tff7b72from T7ee787e
Tff7b72def Td2a8ffrun_database_recoveryTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3actions Tff7b72= Tb4b4b4[Tb4b4b4]
Te6edf3actionsTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffstepTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffquick_check_beforeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffresultTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffquick_checkTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffunknownTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3actionsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffstepTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffwal_checkpointTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffresultTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_checkpoint_walTb4b4b4(Tb4b4b4)Tb4b4b4}Tb4b4b4)
Te6edf3integrity_rows Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffintegrity_checkTb4b4b4(Tb4b4b4)
Te6edf3integrity Tff7b72= Tb4b4b4[Te6edf3rowTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72for Te6edf3row Tff7b72in Te6edf3integrity_rowsTb4b4b4] Tff7b72if Te6edf3integrity_rows Tff7b72else Tb4b4b4[Tb4b4b4]
Te6edf3actionsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffstepTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffintegrity_checkTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffresultTa5d6ff"Tb4b4b4: Te6edf3integrityTb4b4b4}Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffvacuumTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_tune_sqlite_pragmasTb4b4b4(Tb4b4b4)
Te6edf3actionsTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffstepTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffquick_check_afterTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffresultTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Ta5d6ff"Ta5d6ffquick_checkTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffunknownTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffactionsTa5d6ff"Tb4b4b4: Te6edf3actionsTb4b4b4,
Ta5d6ff"Ta5d6ffhealthTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ffget_database_health_snapshotTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ff_checkpoint_and_closeTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_checkpoint_walTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to checkpoint WAL: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to close database: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffcloseTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclose_allTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657hasattrTb4b4b4(Tff7b72selfTb4b4b4, Ta5d6ff"Ta5d6ffproviderTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ff_identity_storage_dirTb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
Te6edf3db_path Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffdb_path
Tff7b72if Tff7b72not Te6edf3db_pathTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffdatabase path is not configuredTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3db_pathTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ff_add_identity_storage_to_zipTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3zfTb4b4b4: Te6edf3zipfileTff7b72.Td2a8ffZipFileTb4b4b4,
Te6edf3db_basenamesTb4b4b4: Tffa657setTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3exclude_pathsTb4b4b4: Tffa657setTb4b4b4[Tffa657strTb4b4b4] Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657strTb4b4b4]Tb4b4b4:
Te6edf3identity_dir Tff7b72= Tff7b72selfTff7b72.Td2a8ff_identity_storage_dirTb4b4b4(Tb4b4b4)
Te6edf3excluded Tff7b72= Tb4b4b4{Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3pathTb4b4b4) Tff7b72for Te6edf3path Tff7b72in Tb4b4b4(Te6edf3exclude_paths Tff7b72or Tb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4}
Te6edf3includedTb4b4b4: Tffa657listTb4b4b4[Tffa657strTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3rootTb4b4b4, Te6edf3dirsTb4b4b4, Te6edf3files Tff7b72in Te6edf3osTff7b72.Td2a8ffwalkTb4b4b4(Te6edf3identity_dirTb4b4b4, Te6edf3followlinksTff7b72=Tff7b72FalseTb4b4b4)Tb4b4b4:
Te6edf3dirsTb4b4b4[Tb4b4b4:Tb4b4b4] Tff7b72= Tb4b4b4[
Te6edf3d
Tff7b72for Te6edf3d Tff7b72in Te6edf3dirs
Tff7b72if Te6edf3d Tff7b72not Tff7b72in Te6edf3BACKUP_SKIP_DIR_NAMES
Tff7b72and Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffislinkTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3rootTb4b4b4, Te6edf3dTb4b4b4)Tb4b4b4)
Tb4b4b4]
Tff7b72for Te6edf3name Tff7b72in Te6edf3filesTb4b4b4:
Tff7b72if Te6edf3name Tff7b72in Te6edf3db_basenames Tff7b72or Te6edf3name Tff7b72== Te6edf3BACKUP_MANIFEST_NAMETb4b4b4:
Tff7b72continue
Te6edf3full_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3rootTb4b4b4, Te6edf3nameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffislinkTb4b4b4(Te6edf3full_pathTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3full_pathTb4b4b4) Tff7b72in Te6edf3excludedTb4b4b4:
Tff7b72continue
Te6edf3rel_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrelpathTb4b4b4(Te6edf3full_pathTb4b4b4, Te6edf3identity_dirTb4b4b4)Tff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3rel_pathTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3zfTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3full_pathTb4b4b4, Te6edf3arcnameTff7b72=Te6edf3rel_pathTb4b4b4)
Te6edf3includedTff7b72.Td2a8ffappendTb4b4b4(Te6edf3rel_pathTb4b4b4)
Tff7b72return Te6edf3included
Tf0883e@staticmethod
Tff7b72def Td2a8ff_safe_zip_extract_memberTb4b4b4(
Te6edf3zfTb4b4b4: Te6edf3zipfileTff7b72.Td2a8ffZipFileTb4b4b4,
Te6edf3memberTb4b4b4: Tffa657strTb4b4b4,
Te6edf3target_dirTb4b4b4: Tffa657strTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e# Normalize separators and reject absolute / parent traversal early.
Te6edf3normalized Tff7b72= Te6edf3memberTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Tb4b4b4(
Tff7b72not Te6edf3normalized
Tff7b72or Te6edf3normalizedTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72or Tffa657anyTb4b4b4(Te6edf3part Tff7b72== Ta5d6ff"Ta5d6ff..Ta5d6ff" Tff7b72for Te6edf3part Tff7b72in Te6edf3normalizedTff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffUnsafe zip entry path: Tffd700{Te6edf3memberTffd700}Ta5d6ff"
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3abs_target Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3target_dirTb4b4b4)
T8b949e# realpath resolves existing symlink prefixes (e.g. identity_dir/plugins -> outside).
Te6edf3abs_dest Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrealpathTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3target_dirTb4b4b4, Te6edf3normalizedTb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3abs_dest Tff7b72!= Te6edf3abs_target Tff7b72and Tff7b72not Te6edf3abs_destTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3abs_target Tff7b72+ Te6edf3osTff7b72.Td2a8ffsepTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffUnsafe zip entry path: Tffd700{Te6edf3memberTffd700}Ta5d6ff"
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3zfTff7b72.Td2a8ffextractTb4b4b4(Te6edf3memberTb4b4b4, Te6edf3target_dirTb4b4b4)
Tff7b72def Td2a8ff_backup_to_zipTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3backup_pathTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3paths Tff7b72= Tff7b72selfTff7b72.Td2a8ff_database_pathsTb4b4b4(Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3backup_pathTb4b4b4)Tb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
T8b949e# ensure WAL is checkpointed to get a consistent snapshot
Tff7b72selfTff7b72.Td2a8ff_checkpoint_walTb4b4b4(Tb4b4b4)
Te6edf3main_filename Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3db_basenames Tff7b72= Tb4b4b4{Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3pTb4b4b4) Tff7b72for Te6edf3p Tff7b72in Te6edf3pathsTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4}
Te6edf3backup_abs Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3backup_pathTb4b4b4)
Tff7b72with Te6edf3zipfileTff7b72.Td2a8ffZipFileTb4b4b4(Te6edf3backup_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4, Te6edf3compressionTff7b72=Te6edf3zipfileTff7b72.Td2a8ffZIP_DEFLATEDTb4b4b4) Tff7b72as Te6edf3zfTb4b4b4:
Te6edf3zfTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3arcnameTff7b72=Te6edf3main_filenameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffwalTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4:
Te6edf3zfTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffwalTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3arcnameTff7b72=Ta5d6fffTa5d6ff"Tffd700{Te6edf3main_filenameTffd700}Ta5d6ff-walTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffshmTa5d6ff"Tb4b4b4]Tb4b4b4)Tb4b4b4:
Te6edf3zfTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffshmTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3arcnameTff7b72=Ta5d6fffTa5d6ff"Tffd700{Te6edf3main_filenameTffd700}Ta5d6ff-shmTa5d6ff"Tb4b4b4)
Te6edf3included Tff7b72= Tff7b72selfTff7b72.Td2a8ff_add_identity_storage_to_zipTb4b4b4(
Te6edf3zfTb4b4b4,
Te6edf3db_basenamesTb4b4b4,
Te6edf3exclude_pathsTff7b72=Tb4b4b4{Te6edf3backup_absTb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3manifest Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: T79c0ff1Tb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffincludes_identity_storageTa5d6ff"Tb4b4b4: Tff7b72TrueTb4b4b4,
Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4: Tffa657sortedTb4b4b4(Te6edf3includedTb4b4b4)Tb4b4b4,
Tb4b4b4}
Te6edf3zfTff7b72.Td2a8ffwritestrTb4b4b4(Te6edf3BACKUP_MANIFEST_NAMETb4b4b4, Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3indentTff7b72=T79c0ff2Tb4b4b4)Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3backup_pathTb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3backup_pathTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffidentity_filesTa5d6ff"Tb4b4b4: Tffa657lenTb4b4b4(Te6edf3includedTb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ff_backup_pre_migrationTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3from_versionTb4b4b4: Tffa657intTb4b4b4, Te6edf3to_versionTb4b4b4: Tffa657intTb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
Te6edf3storage_path Tff7b72= Tff7b72selfTff7b72.Td2a8ff_identity_storage_dirTb4b4b4(Tb4b4b4)
Te6edf3backup_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3backup_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3timestamp Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)Tff7b72.Td2a8ffstrftimeTb4b4b4(Ta5d6ff"Ta5d6ff%Ta5d6ffYTa5d6ff%Ta5d6ffmTffd700%dTa5d6ff-Ta5d6ff%Ta5d6ffHTa5d6ff%Ta5d6ffMTa5d6ff%Ta5d6ffSTa5d6ff"Tb4b4b4)
Te6edf3backup_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3backup_dirTb4b4b4,
Ta5d6fffTa5d6ff"Tffd700{Te6edf3PRE_MIGRATE_BACKUP_PREFIXTffd700}Ta5d6ffvTffd700{Te6edf3from_versionTffd700}Ta5d6ff-to-vTffd700{Te6edf3to_versionTffd700}Ta5d6ff-Tffd700{Te6edf3timestampTffd700}Ta5d6ff.zipTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3result Tff7b72= Tff7b72selfTff7b72.Td2a8ff_backup_to_zipTb4b4b4(Te6edf3backup_pathTb4b4b4)
Tffa657printTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffPre-migration backup written to Tffd700{Te6edf3resultTb4b4b4[Ta5d6ff'Ta5d6ffpathTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff (Tffd700{Te6edf3resultTb4b4b4[Ta5d6ff'Ta5d6ffsizeTa5d6ff'Tb4b4b4]Tffd700}Ta5d6ff bytes)Ta5d6ff"Tb4b4b4,
Te6edf3flushTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3result
Tff7b72def Td2a8fflist_auto_backupsTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4:
Te6edf3backup_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3backup_dirTb4b4b4)Tb4b4b4:
Tff7b72return Tb4b4b4[Tb4b4b4]
Te6edf3backups Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3file Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3backup_dirTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3fileTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3full_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3backup_dirTb4b4b4, Te6edf3fileTb4b4b4)
Te6edf3stats Tff7b72= Te6edf3osTff7b72.Td2a8ffstatTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3backupsTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3fileTb4b4b4,
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3full_pathTb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3statsTff7b72.Td2a8ffst_sizeTb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3datetimeTff7b72.Td2a8fffromtimestampTb4b4b4(
Te6edf3statsTff7b72.Td2a8ffst_mtimeTb4b4b4,
Te6edf3UTCTb4b4b4,
Tb4b4b4)Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Tffa657sortedTb4b4b4(Te6edf3backupsTb4b4b4, Te6edf3keyTff7b72=Tff7b72lambda Te6edf3rowTb4b4b4: Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3reverseTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ffcopy_auto_backupTb4b4b4(
Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4: Tffa657strTb4b4b4, Te6edf3filenameTb4b4b4: Tffa657strTb4b4b4, Te6edf3dest_pathTb4b4b4: Tffa657str
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787path_utils Tff7b72import Te6edf3safe_path_under_dir
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3filenameTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3filename Tff7b72or Ta5d6ff"Tffea00\x00Ta5d6ff" Tff7b72in Te6edf3filenameTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffInvalid backup nameTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3normalized Tff7b72= Te6edf3filenameTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3normalized Tff7b72!= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3normalizedTb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff..Ta5d6ff" Tff7b72in Te6edf3normalizedTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffInvalid backup nameTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3backup_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Te6edf3name Tff7b72= Te6edf3filename Tff7b72if Te6edf3filenameTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4) Tff7b72else Ta5d6fffTa5d6ff"Tffd700{Te6edf3filenameTffd700}Ta5d6ff.zipTa5d6ff"
Te6edf3src Tff7b72= Te6edf3safe_path_under_dirTb4b4b4(Te6edf3backup_dirTb4b4b4, Te6edf3nameTb4b4b4)
Tff7b72if Tff7b72not Te6edf3src Tff7b72or Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3srcTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffBackup not found: Tffd700{Te6edf3nameTffd700}Ta5d6ff"
Tff7b72raise Tf85149FileNotFoundErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3dest_parent Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3dest_parentTb4b4b4:
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3dest_parentTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3shutilTff7b72.Td2a8ffcopy2Tb4b4b4(Te6edf3srcTb4b4b4, Te6edf3dest_pathTb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3nameTb4b4b4,
Ta5d6ff"Ta5d6ffsourceTa5d6ff"Tb4b4b4: Te6edf3srcTb4b4b4,
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3dest_pathTb4b4b4)Tb4b4b4,
Tb4b4b4}
Tff7b72def Td2a8ffbackup_databaseTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3storage_pathTb4b4b4,
Te6edf3backup_pathTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Te6edf3max_countTb4b4b4: Tffa657int Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4)Tb4b4b4:
Te6edf3default_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3default_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3timestamp Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Te6edf3UTCTb4b4b4)Tff7b72.Td2a8ffstrftimeTb4b4b4(Ta5d6ff"Ta5d6ff%Ta5d6ffYTa5d6ff%Ta5d6ffmTffd700%dTa5d6ff-Ta5d6ff%Ta5d6ffHTa5d6ff%Ta5d6ffMTa5d6ff%Ta5d6ffSTa5d6ff"Tb4b4b4)
Te6edf3current_stats Tff7b72= Tff7b72selfTff7b72.Td2a8ff_get_current_db_content_statsTb4b4b4(Tb4b4b4)
Te6edf3baseline Tff7b72= Tff7b72selfTff7b72.Td2a8ff_read_backup_baselineTb4b4b4(Te6edf3storage_pathTb4b4b4)
Te6edf3suspicious Tff7b72= Tff7b72selfTff7b72.Td2a8ff_is_backup_suspiciousTb4b4b4(Te6edf3current_statsTb4b4b4, Te6edf3baselineTb4b4b4)
Tff7b72if Te6edf3backup_path Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3suspiciousTb4b4b4:
Te6edf3backup_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3default_dirTb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffbackup-SUSPICIOUS-Tffd700{Te6edf3timestampTffd700}Ta5d6ff.zipTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3backup_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3default_dirTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffbackup-Tffd700{Te6edf3timestampTffd700}Ta5d6ff.zipTa5d6ff"Tb4b4b4)
Te6edf3result Tff7b72= Tff7b72selfTff7b72.Td2a8ff_backup_to_zipTb4b4b4(Te6edf3backup_pathTb4b4b4)
Tff7b72if Te6edf3suspiciousTb4b4b4:
Te6edf3_logTff7b72.Td2a8ffwarningTb4b4b4(
Ta5d6ff"Ta5d6ffBackup data-loss guard: current DB looks wrong (was Tffd700%sTa5d6ff messages / Tffd700%sTa5d6ff bytes, now Tffd700%sTa5d6ff / Tffd700%sTa5d6ff); Ta5d6ff"
Ta5d6ff"Ta5d6ffwrote backup-SUSPICIOUS-*.zip, skipping rotationTa5d6ff"Tb4b4b4,
Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4)Tb4b4b4,
Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tffa657printTb4b4b4(
Ta5d6ff"Ta5d6ffBackup data-loss guard: current database looks wrong Ta5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ff(was Tffd700{Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6ffmessage_countTa5d6ff'Tb4b4b4)Tffd700}Ta5d6ff messages / Tffd700{Te6edf3baselineTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6fftotal_bytesTa5d6ff'Tb4b4b4)Tffd700}Ta5d6ff bytes, Ta5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ffnow Tffd700{Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6ffmessage_countTa5d6ff'Tb4b4b4)Tffd700}Ta5d6ff / Tffd700{Te6edf3current_statsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6fftotal_bytesTa5d6ff'Tb4b4b4)Tffd700}Ta5d6ff). Ta5d6ff"
Ta5d6ff"Ta5d6ffWrote backup-SUSPICIOUS-*.zip; skipping rotation and baseline update. Check disk and DB.Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3resultTb4b4b4[Ta5d6ff"Ta5d6ffsuspiciousTa5d6ff"Tb4b4b4] Tff7b72= Tff7b72True
Te6edf3resultTb4b4b4[Ta5d6ff"Ta5d6ffbaselineTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3baseline
Te6edf3resultTb4b4b4[Ta5d6ff"Ta5d6ffcurrent_statsTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3current_stats
Tff7b72return Te6edf3result
Tff7b72if Te6edf3max_count Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3max_count Tff7b72> T79c0ff0Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3backups Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3file Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3default_dirTb4b4b4)Tb4b4b4:
Tff7b72if Tb4b4b4(
Te6edf3fileTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4)
Tff7b72and Te6edf3fileTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ffbackup-Ta5d6ff"Tb4b4b4)
Tff7b72and Ta5d6ff"Ta5d6ffSUSPICIOUSTa5d6ff" Tff7b72not Tff7b72in Te6edf3file
Tb4b4b4)Tb4b4b4:
Te6edf3full_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3default_dirTb4b4b4, Te6edf3fileTb4b4b4)
Te6edf3stats Tff7b72= Te6edf3osTff7b72.Td2a8ffstatTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3backupsTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4(Te6edf3full_pathTb4b4b4, Te6edf3statsTff7b72.Td2a8ffst_mtimeTb4b4b4)Tb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3backupsTb4b4b4) Tff7b72> Te6edf3max_countTb4b4b4:
Te6edf3backupsTff7b72.Td2a8ffsortTb4b4b4(Te6edf3keyTff7b72=Tff7b72lambda Te6edf3xTb4b4b4: Te6edf3xTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)
Te6edf3to_delete Tff7b72= Te6edf3backupsTb4b4b4[Tb4b4b4: Tffa657lenTb4b4b4(Te6edf3backupsTb4b4b4) Tff7b72- Te6edf3max_countTb4b4b4]
Tff7b72for Te6edf3pathTb4b4b4, Te6edf3_ Tff7b72in Te6edf3to_deleteTb4b4b4:
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffFailed to cleanup old backups: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_write_backup_baselineTb4b4b4(
Te6edf3storage_pathTb4b4b4,
Te6edf3current_statsTb4b4b4[Ta5d6ff"Ta5d6ffmessage_countTa5d6ff"Tb4b4b4]Tb4b4b4,
Te6edf3current_statsTb4b4b4[Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3result
Tff7b72def Td2a8ffcreate_snapshotTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4, Te6edf3nameTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
T8b949e"""Creates a named snapshot of the database."""
Te6edf3snapshot_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffsnapshotsTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3snapshot_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
T8b949e# Ensure name is safe for filesystem
Te6edf3safe_name Tff7b72= Ta5d6ff"Ta5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(
Tb4b4b4[Te6edf3c Tff7b72for Te6edf3c Tff7b72in Te6edf3name Tff7b72if Te6edf3cTff7b72.Td2a8ffisalnumTb4b4b4(Tb4b4b4) Tff7b72or Te6edf3c Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff_Ta5d6ff"Tb4b4b4)Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3safe_nameTb4b4b4:
Te6edf3safe_name Tff7b72= Ta5d6ff"Ta5d6ffunnamed_snapshotTa5d6ff"
Te6edf3snapshot_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3snapshot_dirTb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3safe_nameTffd700}Ta5d6ff.zipTa5d6ff"Tb4b4b4)
Tff7b72return Tff7b72selfTff7b72.Td2a8ff_backup_to_zipTb4b4b4(Te6edf3snapshot_pathTb4b4b4)
Tff7b72def Td2a8fflist_snapshotsTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3storage_pathTb4b4b4)Tb4b4b4:
T8b949e"""Lists all available snapshots."""
Te6edf3snapshot_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Ta5d6ff"Ta5d6ffsnapshotsTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3snapshot_dirTb4b4b4)Tb4b4b4:
Tff7b72return Tb4b4b4[Tb4b4b4]
Te6edf3snapshots Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3file Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3snapshot_dirTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3fileTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3full_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3snapshot_dirTb4b4b4, Te6edf3fileTb4b4b4)
Te6edf3stats Tff7b72= Te6edf3osTff7b72.Td2a8ffstatTb4b4b4(Te6edf3full_pathTb4b4b4)
Te6edf3snapshotsTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3fileTb4b4b4,
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3full_pathTb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3statsTff7b72.Td2a8ffst_sizeTb4b4b4,
Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4: Te6edf3datetimeTff7b72.Td2a8fffromtimestampTb4b4b4(
Te6edf3statsTff7b72.Td2a8ffst_mtimeTb4b4b4,
Te6edf3UTCTb4b4b4,
Tb4b4b4)Tff7b72.Td2a8ffisoformatTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Tffa657sortedTb4b4b4(Te6edf3snapshotsTb4b4b4, Te6edf3keyTff7b72=Tff7b72lambda Te6edf3xTb4b4b4: Te6edf3xTb4b4b4[Ta5d6ff"Ta5d6ffcreated_atTa5d6ff"Tb4b4b4]Tb4b4b4, Te6edf3reverseTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ffdelete_snapshot_or_backupTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3storage_pathTb4b4b4,
Te6edf3filenameTb4b4b4: Tffa657strTb4b4b4,
Te6edf3is_backupTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4,
Tb4b4b4)Tb4b4b4:
T8b949e"""Deletes a database snapshot or auto-backup."""
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787path_utils Tff7b72import Te6edf3safe_path_under_dir
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3filenameTb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3filename Tff7b72or Ta5d6ff"Tffea00\x00Ta5d6ff" Tff7b72in Te6edf3filenameTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffInvalid pathTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3normalized Tff7b72= Te6edf3filenameTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Tffea00\\Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3normalized Tff7b72!= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3normalizedTb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff..Ta5d6ff" Tff7b72in Te6edf3normalizedTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffInvalid pathTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3base_dir Tff7b72= Ta5d6ff"Ta5d6ffdatabase-backupsTa5d6ff" Tff7b72if Te6edf3is_backup Tff7b72else Ta5d6ff"Ta5d6ffsnapshotsTa5d6ff"
Te6edf3directory Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_pathTb4b4b4, Te6edf3base_dirTb4b4b4)
Te6edf3name Tff7b72= Te6edf3filename Tff7b72if Te6edf3filenameTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.zipTa5d6ff"Tb4b4b4) Tff7b72else Ta5d6fffTa5d6ff"Tffd700{Te6edf3filenameTffd700}Ta5d6ff.zipTa5d6ff"
Te6edf3abs_path Tff7b72= Te6edf3safe_path_under_dirTb4b4b4(Te6edf3directoryTb4b4b4, Te6edf3nameTb4b4b4)
Tff7b72if Tff7b72not Te6edf3abs_pathTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffInvalid pathTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3abs_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3abs_pathTb4b4b4)
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tf0883e@staticmethod
Tff7b72def Td2a8ff_looks_like_sqliteTb4b4b4(Te6edf3pathTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Tff7b72return Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(T79c0ff16Tb4b4b4) Tff7b72== Ta5d6ffbTa5d6ff"Ta5d6ffSQLite format 3Tffea00\x00Ta5d6ff"
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffrestore_databaseTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3backup_pathTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3backup_pathTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffBackup not found at Tffd700{Te6edf3backup_pathTffd700}Ta5d6ff"
Tff7b72raise Tf85149FileNotFoundErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3paths Tff7b72= Tff7b72selfTff7b72.Td2a8ff_database_pathsTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_checkpoint_and_closeTb4b4b4(Tb4b4b4)
Te6edf3target_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3main_name Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4)
Te6edf3staging_dir Tff7b72= Te6edf3tempfileTff7b72.Td2a8ffmkdtempTb4b4b4(Te6edf3prefixTff7b72=Ta5d6ff"Ta5d6ff.meshchatx-restore-Ta5d6ff"Tb4b4b4, Tffa657dirTff7b72=Te6edf3target_dirTb4b4b4)
Te6edf3integrity_rows Tff7b72= Tff7b72None
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3zipfileTff7b72.Td2a8ffis_zipfileTb4b4b4(Te6edf3backup_pathTb4b4b4)Tb4b4b4:
Tff7b72with Te6edf3zipfileTff7b72.Td2a8ffZipFileTb4b4b4(Te6edf3backup_pathTb4b4b4, Ta5d6ff"Ta5d6ffrTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3zfTb4b4b4:
Tff7b72for Te6edf3member Tff7b72in Te6edf3zfTff7b72.Td2a8ffnamelistTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3memberTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72selfTff7b72.Td2a8ff_safe_zip_extract_memberTb4b4b4(Te6edf3zfTb4b4b4, Te6edf3memberTb4b4b4, Te6edf3staging_dirTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffcopy2Tb4b4b4(Te6edf3backup_pathTb4b4b4, Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3staging_dirTb4b4b4, Te6edf3main_nameTb4b4b4)Tb4b4b4)
Te6edf3staging_main Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3staging_dirTb4b4b4, Te6edf3main_nameTb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3staging_mainTb4b4b4)Tb4b4b4:
T8b949e# Zip may use a different basename for the primary db file.
Te6edf3candidates Tff7b72= Tb4b4b4[
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3staging_dirTb4b4b4, Te6edf3nameTb4b4b4)
Tff7b72for Te6edf3name Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3staging_dirTb4b4b4)
Tff7b72if Te6edf3nameTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.dbTa5d6ff"Tb4b4b4) Tff7b72and Tff7b72not Te6edf3nameTff7b72.Td2a8ffendswithTb4b4b4(Tb4b4b4(Ta5d6ff"Ta5d6ff-walTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-shmTa5d6ff"Tb4b4b4)Tb4b4b4)
Tb4b4b4]
Tff7b72if Tffa657lenTb4b4b4(Te6edf3candidatesTb4b4b4) Tff7b72== T79c0ff1Tb4b4b4:
Te6edf3staging_main Tff7b72= Te6edf3candidatesTb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72elseTb4b4b4:
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(
Ta5d6ff"Ta5d6ffBackup does not contain a primary database fileTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Tff7b72selfTff7b72.Td2a8ff_looks_like_sqliteTb4b4b4(Te6edf3staging_mainTb4b4b4)Tb4b4b4:
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(
Ta5d6ff"Ta5d6ffRestored file is not a valid SQLite databaseTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3aside_dir Tff7b72= Te6edf3tempfileTff7b72.Td2a8ffmkdtempTb4b4b4(
Te6edf3prefixTff7b72=Ta5d6ff"Ta5d6ff.meshchatx-aside-Ta5d6ff"Tb4b4b4,
Tffa657dirTff7b72=Te6edf3target_dirTb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72for Te6edf3keyTb4b4b4, Te6edf3live_path Tff7b72in Te6edf3pathsTff7b72.Td2a8ffitemsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3live_pathTb4b4b4)Tb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffmoveTb4b4b4(
Te6edf3live_pathTb4b4b4,
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3aside_dirTb4b4b4, Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3live_pathTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3shutilTff7b72.Td2a8ffmoveTb4b4b4(Te6edf3staging_mainTb4b4b4, Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72for Te6edf3suffixTb4b4b4, Te6edf3key Tff7b72in Tb4b4b4(Tb4b4b4(Ta5d6ff"Ta5d6ff-walTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffwalTa5d6ff"Tb4b4b4)Tb4b4b4, Tb4b4b4(Ta5d6ff"Ta5d6ff-shmTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffshmTa5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3staged Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3staging_dirTb4b4b4,
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3pathsTb4b4b4[Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72+ Te6edf3suffixTb4b4b4,
Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3stagedTb4b4b4)Tb4b4b4:
Te6edf3staged Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3staging_dirTb4b4b4,
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3staging_mainTb4b4b4) Tff7b72+ Te6edf3suffixTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3stagedTb4b4b4)Tb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffmoveTb4b4b4(Te6edf3stagedTb4b4b4, Te6edf3pathsTb4b4b4[Te6edf3keyTb4b4b4]Tb4b4b4)
T8b949e# Copy any remaining identity-storage files from the zip staging tree.
Tff7b72for Te6edf3rootTb4b4b4, Te6edf3_dirsTb4b4b4, Te6edf3files Tff7b72in Te6edf3osTff7b72.Td2a8ffwalkTb4b4b4(Te6edf3staging_dirTb4b4b4, Te6edf3followlinksTff7b72=Tff7b72FalseTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3name Tff7b72in Te6edf3filesTb4b4b4:
Te6edf3src Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3rootTb4b4b4, Te6edf3nameTb4b4b4)
Te6edf3rel Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffrelpathTb4b4b4(Te6edf3srcTb4b4b4, Te6edf3staging_dirTb4b4b4)
Tff7b72if Te6edf3rel Tff7b72in Tb4b4b4{Te6edf3main_nameTb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3main_nameTffd700}Ta5d6ff-walTa5d6ff"Tb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3main_nameTffd700}Ta5d6ff-shmTa5d6ff"Tb4b4b4}Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3nameTff7b72.Td2a8ffendswithTb4b4b4(Tb4b4b4(Ta5d6ff"Ta5d6ff-walTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff-shmTa5d6ff"Tb4b4b4)Tb4b4b4) Tff7b72and Te6edf3nameTff7b72.Td2a8ffstartswithTb4b4b4(
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffsplitextTb4b4b4(Te6edf3main_nameTb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3dest Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3target_dirTb4b4b4, Te6edf3relTb4b4b4)
Te6edf3dest_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3destTb4b4b4)
Tff7b72if Te6edf3dest_dirTb4b4b4:
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3dest_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffislinkTb4b4b4(Te6edf3srcTb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3shutilTff7b72.Td2a8ffcopy2Tb4b4b4(Te6edf3srcTb4b4b4, Te6edf3destTb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tff7b72selfTff7b72.Td2a8ff_restore_aside_filesTb4b4b4(Te6edf3aside_dirTb4b4b4, Te6edf3pathsTb4b4b4)
Tff7b72with Te6edf3suppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffRestored files from backup but database failed to open: Tffd700{Te6edf3excTffd700!s}Ta5d6ff"Tb4b4b4,
Tb4b4b4) Tff7b72from T7ee787exc
Tff7b72selfTff7b72.Td2a8ff_tune_sqlite_pragmasTb4b4b4(Tb4b4b4)
Te6edf3integrity_rows Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffintegrity_checkTb4b4b4(Tb4b4b4)
Te6edf3integrity Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3row Tff7b72in Te6edf3integrity_rows Tff7b72or Tb4b4b4[Tb4b4b4]Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3rowTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3integrityTff7b72.Td2a8ffappendTb4b4b4(Tffa657nextTb4b4b4(Tffa657iterTb4b4b4(Te6edf3rowTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3integrityTff7b72.Td2a8ffappendTb4b4b4(Te6edf3rowTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4)
Tff7b72if Te6edf3integrity Tff7b72and Te6edf3integrityTb4b4b4[T79c0ff0Tb4b4b4] Tff7b72!= Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ff_restore_aside_filesTb4b4b4(Te6edf3aside_dirTb4b4b4, Te6edf3pathsTb4b4b4)
Tff7b72with Te6edf3suppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)
Tff7b72raise Te6edf3DatabaseRestoreErrorTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffRestored backup failed integrity check: Tffd700{Te6edf3integrityTb4b4b4[T79c0ff0Tb4b4b4]Tffd700!s}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Te6edf3aside_dirTb4b4b4, Te6edf3ignore_errorsTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Te6edf3staging_dirTb4b4b4, Te6edf3ignore_errorsTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffrestored_fromTa5d6ff"Tb4b4b4: Te6edf3backup_pathTb4b4b4,
Ta5d6ff"Ta5d6ffintegrity_checkTa5d6ff"Tb4b4b4: Te6edf3integrity_rowsTb4b4b4,
Ta5d6ff"Ta5d6ffhealthTa5d6ff"Tb4b4b4: Tff7b72selfTff7b72.Td2a8ffget_database_health_snapshotTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4}
Tf0883e@staticmethod
Tff7b72def Td2a8ff_restore_aside_filesTb4b4b4(Te6edf3aside_dirTb4b4b4: Tffa657strTb4b4b4, Te6edf3pathsTb4b4b4: Tffa657dictTb4b4b4) Tff7b72-Tff7b72> Tff7b72NoneTb4b4b4:
T8b949e"""Put previously moved live DB files back after a failed restore."""
Tff7b72for Te6edf3live_path Tff7b72in Te6edf3pathsTff7b72.Td2a8ffvaluesTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3aside Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3aside_dirTb4b4b4, Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3live_pathTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3asideTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3live_pathTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3live_pathTb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72pass
Tff7b72tryTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffmoveTb4b4b4(Te6edf3asideTb4b4b4, Te6edf3live_pathTb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72pass
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────